set.seed(123)
df <- read.arff("data/Medicaldataset.arff")
df <- df %>% rename(
"Age" = age,
"Gender" = gender,
"Heart Rate" = impluse,
"Systolic.blood.pressure" = pressurehight,
"Diastolic.blood.pressure" = pressurelow,
"Blood.sugar" = glucose,
"CK.MB" = kcm,
"Troponin" = troponin,
"Result" = class
)Analiza zbioru danych pacjentów podejrzewających u siebie zawał serca
Źródło danych
Zawartość zbioru
Dane do zbioru były zbierane podczas transportu karetką do szpitala pacjentów, którzy podejrzewali u siebie obecność zawału serca. Zawał mięśnia sercowego, potocznie zwany atakiem serca to martwica mięśnia sercowego spowodowana jego niedokrwieniem wskutek zamknięcia tętnicy wieńcowej doprowadzającej krew do serca. W Europie choroby układu krążenia są najczęstszą przyczyną zgonów. Z powodu schorzeń sercowo-naczyniowych w tym zawału mięśnia sercowego, umiera dwa razy więcej osób niż z powodu chorób nowotworowych.(“Eurostat” 2006) Zbiór danych zawiera 1319 obserwacji i 9 cech, z czego jedna odnosi się do obecności ataku serca.
Wiek, płeć, tętno, skurczowe ciśnienie tętnicze, rozkurczowe ciśnienie tętnicze, poziom cukru we krwi, CK-MB (kineaza kreatynowa) i troponina reprezentują pola wejściowe, podczas gdy pole wyjściowe odnosi się do obecności zawału serca, który jest podzielone na dwie kategorie (0 i 1); “0” odnosi się do braku zawału serca, podczas gdy “1” odnosi się do obecności zawału serca.
Przedstawienie zmiennych
| Cecha | Opis |
|---|---|
| Age | Wiek pacjenta w latach |
| Gender | Płeć pacjenta (0 - kobieta, 1 - mężczyzna) |
| Heart Rate | Maksymalne osiągnięte tętno (za prawidłowe tętno uznaje się przedział od 60 do 100 uderzeń serca na minutę) |
| Systolic blood pressure | Spoczynkowe skurczowe ciśnienie krwi (w mmHg, podczas dojazdu do szpitala) (normalne wartości 90-120 mmHg) |
| Diastolic blood pressure | Spoczynkowe rozkurczowe ciśnienie krwi (w mmHg, podczas dojazdu do szpitala) (normalne wartości od 60-80 mmHg) |
| Blood sugar | Poziom cukru we krwi (normalne wartości do 140mg/dl) |
| CK-MB | Enzym CK-MB (mężczyźni do 7,8 ng/mL, kobiety do 4,4 ng/mL). Jest to enzym kinazy kreatynowej. |
| Troponin | Enzym troponiny (wartości normalne do 0,03 mikrogramów) |
| Result | Obecność zawału (0 - brak zawału, 1 - zawał) |
Cele badawcze
Opracowanie modelu predykcyjnego, który na podstawie danych o pacjencie (wiek, płeć, tętno, ciśnienie krwi, poziom cukru we krwi, CK-MB, troponina), przewiduje czy miał on zawał serca.
Porównanie wartości cech którymi kieruje się model (m.in. CKMB i Troponina) do wartości referencyjnych stosowanych przez lekarzy do wykrycia zawału.
Używając technik wizualizacji danych wykryć wzorce i nietypowe zachowania w zbiorze danych, a także lepiej zrozumieć rozkłady poszczególnych zmiennych.
Przygotowanie zbioru, weryfikacja poprawnosci danych, brakujace wartosci
nas <- colSums(is.na(df))
kable(nas, col.names = "Liczba braków danych")| Liczba braków danych | |
|---|---|
| Age | 0 |
| Gender | 0 |
| Heart Rate | 0 |
| Systolic.blood.pressure | 0 |
| Diastolic.blood.pressure | 0 |
| Blood.sugar | 0 |
| CK.MB | 0 |
| Troponin | 0 |
| Result | 0 |
W żadnej z kolumn nie występują braki danych.
# zmiana cech na factor
df$Result <- ifelse(df$Result == "positive", 1, 0)
df$Gender <- as.factor(df$Gender)
df$Result <- as.factor(df$Result)
df %>% get_summary_stats(show = c("min", "median","mean", "sd", "max")) %>% select(-n) %>% pander()| variable | min | median | mean | sd | max |
|---|---|---|---|---|---|
| Age | 14 | 58 | 56.19 | 13.65 | 103 |
| Heart Rate | 20 | 74 | 78.34 | 51.63 | 1111 |
| Systolic.blood.pressure | 42 | 124 | 127.2 | 26.12 | 223 |
| Diastolic.blood.pressure | 38 | 72 | 72.27 | 14.03 | 154 |
| Blood.sugar | 35 | 116 | 146.6 | 74.92 | 541 |
| CK.MB | 0.321 | 2.85 | 15.27 | 46.33 | 300 |
| Troponin | 0.001 | 0.014 | 0.361 | 1.155 | 10.3 |
Zmienna Heart Rate zawiera watości maksymalne 1111, które są błędem. Sprawdzimy dokładniej tę zmienną pod kątem wartości odstających.
| Heart Rate | is.outlier | is.extreme |
|---|---|---|
| 1111 | TRUE | TRUE |
| 1111 | TRUE | TRUE |
| 1111 | TRUE | TRUE |
Występują trzy obserwacje, w których tętno wynosi 1111. Normalne tętno wynosi maksymalnie 100 uderzeń serca na minutę więc są to nieprawidłowe dane, najprawdopodobniej spowodowane błędem przy wpisywaniu danych, które należy usunąć.
Opis zbioru
Cechy jakościowe
| Result | Gender |
|---|---|
| Positive:509 | Female:447 |
| Negative:807 | Male :869 |
W zbiorze jest 1316 obserwacji. Cech jakościowych jest dwie. Osób z zawałem serca jest 807, natomiast 509 ma wynik negatywny. Kobiet w zbiorze jest 447, a mężczyzn 869.
Cechy ilościowe
| variable | min | median | mean | sd | max |
|---|---|---|---|---|---|
| Age | 14 | 58 | 56.21 | 13.64 | 103 |
| Heart Rate | 20 | 74 | 75.98 | 15.28 | 135 |
| Systolic.blood.pressure | 42 | 124 | 127.1 | 26.14 | 223 |
| Diastolic.blood.pressure | 38 | 72 | 72.22 | 14.01 | 154 |
| Blood.sugar | 35 | 116 | 146.7 | 74.98 | 541 |
| CK.MB | 0.321 | 2.85 | 15.3 | 46.38 | 300 |
| Troponin | 0.001 | 0.014 | 0.361 | 1.156 | 10.3 |
Zbiór danych przedstawia zróżnicowany zakres wskaźników medycznych, odzwierciedlających parametry związane ze zdrowiem poszczególnych osób. Wiek waha się od 14 do 103 lat, co wskazuje na szeroki rozkład wiekowy. Pomiary tętna i ciśnienia krwi wykazują typowe wartości, z umiarkowaną zmiennością. W szczególności poziom cukru we krwi wykazuje znaczną zmienność, ze średnią 146,7 i znacznym odchyleniem standardowym 74,98. Markery CK.MB i Troponina, związane ze zdrowiem serca, wykazują znaczną zmienność, szczególnie w przypadku CK.MB, gdzie średnia jest znacznie wyższa niż mediana, co sugeruje potencjalną skośność. Te spostrzeżenia podkreślają znaczenie zbadania charakterystyki rozkładu każdej zmiennej dla lepszego zrozumienia zbioru danych.
Wizualizacja
Piramida wieku
Na powyższym wykresie widać, że rozkłady wieku wyglądają podobnie zarówno dla mężczyzn jak i kobiet, jednak rozkład wieku wśród płci żeńskiej charakteryzuje się znacznie mniejszą kurtozą. Najwięcej pacjentów znajduje się w przedziale wiekowym 60-64, co stanowi około 18% całego zbioru. Widzimy też, że liczebność mężczyzn jest zdecydowanie większa w przedziałach z środkowego obszaru, natomiast liczebość kobiet zaczyna minimalnie przeważać wśród osób najmłodszych i najstarszych.
Histogram wieku z podziałem na płeć oraz obecność ataku serca
Wyraźnie widać, że rozkład mężczyzn z atakiem serca (lewy dolny histogram) przypomina rozkład normalny. Natomiast pozostałe 3 wykresy są do siebie podobne i także przypominają rozkład normalny, tylko z o wiele mniejszą kurtozą. Możemy zauważyć, że różnica między brakiem, a istnieniem zawału serca jest o wiele większa wśród mężczyzn. Naszą hipotezę sprawdzimy testem proporcji.
Test proporcji dla obecności ataku serca ze względu na płeć
Hipoteza zerowa zakłada, że procent pozytywnych przypadków ataku serca jest taki sam dla obu płci. Hipoteza alternatywna zakłada, że procent pozytywnych przypadków ataku serca jest różny dla obu płci.
# Tworzenie tablicy przestawnej
table <- table(df$Gender, df$Result)
# Przeprowadzenie testu proporcji (z testem chi-kwadrat)
result_prop_test <- prop.test(table)
chi_square_test <- chisq.test(table)
data.frame("Proportion test"=round(result_prop_test$p.value,3),
"Chi-Square test"=round(chi_square_test$p.value,3)) %>% pander()| Proportion.test | Chi.Square.test |
|---|---|
| 0.001 | 0.001 |
Na podstawie testu chi-kwadrat jak i test proporcjonalności odrzucamy hipotezę o rowności procenta pozytywnych przypadków ataku serca ze względu na płeć. Wynika z tego, że większą szansę na atak serca mają mężczyźni.
Wykres korelacji cech
Dla przypadków z atakiem serca
Dla przypadków bez ataku serca
Powyższe wykresy są bardzo do siebie podobne. Jedyną wyraźną cechą różniącą je jest cecha CK.MB, która dla zdrowych pacjentów jest w zakresie 0-7, a dla osób z zawałem serca od 0-300. Warto również zwrócić uwagę na to, że stosunkowo silna korelacja występuje jedynie pomiędzy zmienną przechowującą ciśnienie skurczowe oraz rozkurczowe, co ma sens, ponieważ w praktyce miary te również są ze sobą powiązane. Wyklucza to jednak możliwość użycia obydwu zmiennych w przyszłych modelach.
Histogram dla każdej cechy, z wizualnym podziałem na wynik
Wykres zależności CK.MB od Troponiny z podziałem na pacjentów z zawałem i bez
Na powyższym wykresie zależności CK.MB od Troponiny widać wyraźną granicę między pacjentami z zawałem serca i bez. Widać, że pacjenci z zawałem serca mają wyższe wartości obu cech. Może nam to sugerować że są to cechy, które mogą być przydatne w modelu.
Model
Wybór modelu
Z racji tego, że nasz problem jest klasyfikacyjny, postanowiliśmy porównać ze sobą 3 modele:
drzewo decyzyjne dla wszystkich zmiennych,
drzewo decyzyjne bez CK.MB oraz Troponiny,
regresja logistyczna dla CK.MB i Troponiny,
regresja logistyczna dla reszty zmiennych
Modele z CK.MB i Troponin
Model regresji logistycznej
sample <- sample(c(TRUE, FALSE), nrow(df), replace=TRUE, prob=c(0.7,0.3))
test <- df[!sample, ]
df <- df[sample, ]
modelbezlog <- glm(Result~CK.MB+Troponin, family = 'binomial', data = df)
summary(modelbezlog)
Call:
glm(formula = Result ~ CK.MB + Troponin, family = "binomial",
data = df)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -1.27773 0.15143 -8.438 < 2e-16 ***
CK.MB 0.30474 0.03872 7.871 3.52e-15 ***
Troponin 4.43410 0.68464 6.477 9.38e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1238.55 on 927 degrees of freedom
Residual deviance: 853.99 on 925 degrees of freedom
AIC: 859.99
Number of Fisher Scoring iterations: 10
p1 <- ggplot(df, aes(x = CK.MB, y = Troponin)) + geom_point() + ggtitle("Zmienne przed transformacją")
p2 <- ggplot(df, aes(x = log(CK.MB), y = log(Troponin))) + geom_point() + ggtitle("Zmienne po transformacji")
grid.arrange(p1, p2, ncol = 2)model <- glm(Result~log(CK.MB)+log(Troponin), family = 'binomial', data = df)
summary(model)
Call:
glm(formula = Result ~ log(CK.MB) + log(Troponin), family = "binomial",
data = df)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 9.1335 0.8201 11.14 <2e-16 ***
log(CK.MB) 2.0472 0.1926 10.63 <2e-16 ***
log(Troponin) 2.5355 0.1987 12.76 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1238.55 on 927 degrees of freedom
Residual deviance: 435.15 on 925 degrees of freedom
AIC: 441.15
Number of Fisher Scoring iterations: 7
Testowanie założeń:
1) Zmienna objaśniająca przyjmuje dwie wartości (jest zmienną binarną)
b <- as.data.frame(summary(df$Result))
colnames(b) <- c("Result")
kable(b)| Result | |
|---|---|
| 0 | 359 |
| 1 | 569 |
Założenie jest spełnione, ponieważ zmienna Result przyjmuje tylko dwie wartości: 0 oraz 1.
2) Niezależność obserwacji
Każda obserwacja odpowiada parametrom innego pacjenta, zatem założenie jest spełnione.
3) Brak współliniowości pomiędzy zmiennymi niezależnymi.
mtest <- vif(model)
kable(mtest)| x | |
|---|---|
| log(CK.MB) | 1.465035 |
| log(Troponin) | 1.465035 |
Współczynniki testu na współliniowość są bliskie 1, więc nie mamy powodów do odrzucenia hipotezy o braku współliniowości.
4) Brak ekstremalnych wartości odstających.
identify_outliers(log(df['CK.MB'])) %>%
as.data.frame() %>%
filter(is.extreme == TRUE) %>%
head() %>%
kable()| CK.MB | is.outlier | is.extreme |
|---|---|---|
| 5.703782 | TRUE | TRUE |
| 5.703782 | TRUE | TRUE |
| 5.703782 | TRUE | TRUE |
| 5.703782 | TRUE | TRUE |
| 5.659134 | TRUE | TRUE |
| 5.703782 | TRUE | TRUE |
Wartości zmiennej CK.MB mają wartości ekstremalne, pomimo to, zdecydujemy się zachować te obserwacje w zbiorze danych i zbudować na ich podstawie model.
5) Odpowiednio duża liczebność próby.
Zbiór zawiera 927 obserwacji, co jest wystarczającą ilością do zbudowania na jego podstawie modelu.
probs <- predict(model, newdata = test, type = "response")
pred <- rep(0, length(probs))
pred[probs > .5] <- 1
table1 <- table(pred, test$Result)
cfm1 <- as.data.frame(table1)
plot_confusion_matrix(cfm1,
target_col = "Var2",
prediction_col = "pred",
counts_col = "Freq")log_acc1 <- sum(diag(table1))/sum(table1)Ten model ma dokładność na poziomie 91.24%.
Model drzewa decyzyjnego
Użycie cross validation do trenowania modelu
Podział zbioru danych (80% dane treningowe i 20% dane testowe)
podzial <- createDataPartition(y = df$Result,
times = 1,
p = 0.8,
list = F)
train <- df[podzial, ]
test <- df[-podzial, ]# Trenowanie modelu z użyciem cross validation
model2 <- caret::train(x= train[-9],
y=train[,9],
method = 'rpart',
trControl = trainControl(method = 'cv', number=5))# Testowanie modelu i wyświetlenie tabeli pomyłek
model_test <- predict(model2, test)
model_test <- as.data.frame(model_test)
table2 <- table(test$Result, model_test[,'model_test'])
tree_acc1 <- sum(diag(table2))/sum(table2)
cfm2 <- as.data.frame(table2)
plot_confusion_matrix(cfm2,
target_col = "Var2",
prediction_col = "Var1",
counts_col = "Freq")log_acc2 <- sum(diag(table2))/sum(table2)Ten model ma dokładność na poziomie 97.83%.
fancyRpartPlot(model2$finalModel, caption='')Modele bez zmiennych CK.MB i Troponin
df2 <- df %>%
select(Age, Gender, `Heart Rate`, Systolic.blood.pressure, Diastolic.blood.pressure, Blood.sugar, Result)Drzewo decyzyjne
podzial2 <- createDataPartition(y = df2$Result,
times = 1,
p = 0.8,
list = F)
train2 <- df2[podzial2, ]
test2 <- df2[-podzial2, ]
model3 <- rpart(Result ~ ., method = "class", data = train2, minsplit = 100)
fancyRpartPlot(model3, caption = "")Dokładność
model_test2 <- predict(model3, test2)
model_test2 <- as.data.frame(model_test2)
model_test2 <- model_test2 %>%
mutate(Result = ifelse(`0` >= .5, 0, 1))
tab3 <- table(test2$Result, model_test2$Result)
tab3 <- as.matrix(tab3)
tree_acc2 <- sum(diag(tab3))/sum(tab3)
cfm3 <- as.data.frame(tab3)
plot_confusion_matrix(cfm3,
target_col = "Var2",
prediction_col = "Var1",
counts_col = "Freq")log_acc3 <- sum(diag(tab3))/sum(tab3)Ten model ma dokładność na poziomie 62.5%.
Regresja logistyczna
model4 <- glm(Result~., family = 'binomial', data = df2)
summary(model4)
Call:
glm(formula = Result ~ ., family = "binomial", data = df2)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.3175000 0.6531301 -3.548 0.000388 ***
Age 0.0435729 0.0055242 7.888 3.08e-15 ***
Gender1 0.3439644 0.1490334 2.308 0.021001 *
`Heart Rate` 0.0057899 0.0047786 1.212 0.225651
Systolic.blood.pressure -0.0048850 0.0033179 -1.472 0.140927
Diastolic.blood.pressure 0.0078084 0.0063718 1.225 0.220405
Blood.sugar -0.0015472 0.0009091 -1.702 0.088794 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1238.5 on 927 degrees of freedom
Residual deviance: 1160.9 on 921 degrees of freedom
AIC: 1174.9
Number of Fisher Scoring iterations: 4
probs <- predict(model4, newdata = test2, type = "response")
pred <- rep(0, length(probs))
pred[probs > .5] <- 1
tab4 <- table(pred, test2$Result)
log_acc2 <- sum(diag(tab4))/sum(tab4)
cfm4 <- as.data.frame(tab4)
plot_confusion_matrix(cfm4,
target_col = "Var2",
prediction_col = "pred",
counts_col = "Freq")log_acc4 <- sum(diag(tab4))/sum(tab4)Ten model ma dokładność na poziomie 63.04%.
W modelu regresji logistycznej tylko jedna zmienna jest statystycznie istotne (age) i oczywiście nie jest ona wytłumaczyć czy ktoś ma zawał serca tak dobrze jak w modelu drzewa decyzyjnego.
Podsumowanie efektywności modelu / jego przydatności
modele <- c("Drzewo decyzyjne z CK.MB i Troponin", "Drzewo decyzyjne bez CK.MB i Troponin", "Regresja logistyczna z CK.MB i Troponin", "Regresja logistyczna bez CK.MB i Troponin")
dokladnosci <- c(tree_acc1, tree_acc2, log_acc1, log_acc2)
dokladnosc <- data.frame(modele, dokladnosci)
colnames(dokladnosc) <- c("Model", "Dokładność")
dokladnosc$Dokładność <- round(dokladnosc$Dokładność, 2)
kable(dokladnosc)| Model | Dokładność |
|---|---|
| Drzewo decyzyjne z CK.MB i Troponin | 0.98 |
| Drzewo decyzyjne bez CK.MB i Troponin | 0.62 |
| Regresja logistyczna z CK.MB i Troponin | 0.91 |
| Regresja logistyczna bez CK.MB i Troponin | 0.63 |
Porównanie modeli regresji logistycznej
modelsummary(list("Model z CK.MB i Troponiną (po transformacji)" = model, "Model z CK.MB i Troponiną (przed transformacją)" = modelbezlog, "Model złożony z pozostałych zmiennych" = model4), coef_omit = 2:11)| Model z CK.MB i Troponiną (po transformacji) | Model z CK.MB i Troponiną (przed transformacją) | Model złożony z pozostałych zmiennych | |
|---|---|---|---|
| (Intercept) | 9.134 | -1.278 | -2.317 |
| (0.820) | (0.151) | (0.653) | |
| Num.Obs. | 928 | 928 | 928 |
| AIC | 441.2 | 860.0 | 1174.9 |
| BIC | 455.7 | 874.5 | 1208.7 |
| Log.Lik. | -217.576 | -426.996 | -580.444 |
| F | 90.067 | 46.984 | 11.595 |
| RMSE | 0.26 | 0.38 | 0.47 |
Wnioski
Okazuje się, że drzewo decyzyjne ze zmiennymi CK.MB i Troponin jest najlepszym modelem, ponieważ ma największą dokładność.
Zaskoczeniem było dla nas to, że reszta zmiennych ma tak mały wpływ na to czy pacjent ma zawał serca.
Jednakże, patrząc na to, czym kierują się lekarze, przy badaniu pacjenta, to właśnie wartości tych dwóch czynników są najważniejsze i decydujące o istnieniu zawału.
Drzewo decyzyjne używając tych dwóch zmiennych jest w stanie bardzo dobrze przewidzieć czy pacjent ma zawał serca, ponieważ jak widać na wykresie zależności CK.MB od Troponiny, punkty oznaczające pacjentów z wynikiem negatywnym układają się w wyraźny prostokąt, dookoła którego są pacjenci z wynikiem pozytywnym.
ggplotly(p)Modele stworzone bez zmiennych CK.MB i Troponiny mają o wiele gorszą dokładność. Jedyna zmienna, która w istotny sposób wpływa na wynik jest wiek, ale nie jest ona w stanie przewidzieć czy pacjent ma zawał serca tak dobrze jak w modelu ze zmiennymi CK.MB i Troponin.
Być może gdybyśmy w dostępnych danych mieli takie wartości jak:
czy dana osoba pali papierosy,
poziom cholesterolu,
czy dana osoba jest aktywna fizycznie,
to moglibyśmy stworzyć lepszy model, który miałby większą dokładność.
Odpowiedź na pytania badawcze
Opracowanie modelu predykcyjnego, który na podstawie danych o pacjencie, przewiduje czy miał on zawał serca.
Udało nam się stworzyć model i uzyskać wysoką dokładność. Jednak przy większej i lepszej jakości danych, model mógłby być jeszcze lepszy.
Porównanie wartości cech którymi kieruje się model (m.in. CKMB i Troponina) do wartości referencyjnych stosowanych przez lekarzy do wykrycia zawału.
Lekarze stwierdzając świeży zawał mięśnia sercowego, stosują wartość odcięcia (czyli wartość powyżej 99 centyla zakresu referencyjnego). Dla CK.MB wynosi ona około \(5.5 ng/ml\) ,a dla troponiny wartość odcięcia wacha się od \(0.01 \mu g/l\) do \(0.08 \mu g/l\) w zależności od producenta testu.(Jaffe 2006)
W naszym modelu wartość, od której decydujemy czy ktoś ma zawał dla troponiny wynosi \(0.015 \mu g/l\), a dla CK.MB \(6.3 ng/ml\).
Zatem wartość troponiny w naszym modelu mieści się w dolnej granicy wartości referencyjnych stosowanych przez lekarzy. Natomiast model wymaga nieco większej wartości enzymu kineazy kreatynowej do stwierdzenia zawału serca niż sugerują to wartości referencyjne.
Używając technik wizualizacji danych wykryć wzorce i nietypowe zachowania w zbiorze danych, a także lepiej zrozumieć rozkłady poszczególnych zmiennych.
Wizualizacja danych pozwoliła nam zauważyć błędy w danych, po pozbyciu się których, otrzymaliśmy lepsze jakościowo dane. Dostrzegliśmy także zmienne, które mogły mieć dużą moc predykcyjną w stworzonym później modelu. Dowiedzieliśmy się także, że największą grupą w zbiorze są mężczyźni z zawałem serca, a najmniej liczna jest grupa kobiet bez zawału serca.
Podsumowanie
Dzięki temu projektowi wykorzystaliśmy zdobyte na studiach wiadomości i umiejętności z zakresu analizy danych na prawdziwym zbiorze danych. Dokonaliśmy szczegółowej wizualizacji danych, dzięki której lepiej poznaliśmy nasze dane. Udało nam się także stworzyć model decyzyjny, który osiągnął wysoką skuteczność.